﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using COC.CEvent;
using COC.Exception;
using COC.Tool;
using UnityEngine;
using Object = System.Object;
using ThreadPriority = System.Threading.ThreadPriority;

namespace COC.Threading
{
    public class CThreadPool
    {

        List<CThread> activeThreads = new List<CThread>(); //record thread as active when a thread is running.

        List<CThread> inactiveThreads = new List<CThread>();//record thread as inactive when stop or subspend a thread.
        string identifiedName;
        CThreadPoolInfo mThreadPoolInfo;
        CEventQueue mDoQueue;
        CEventQueue mFutureQueue;
        CThreadPoolInfo.ThreadPoolToken mThreadPoolToken;
        public readonly object _Lock = new object();
        int poolSize = 2;
        bool cancel;
        private CThreadFactory threadFactory;

        void ActiveMain()
        {

            CThread[] threadPool = new CThread[poolSize];
            CThread.CThreadOption threadOptions = new CThread.CThreadOption();
            threadOptions.mDoQueue = mDoQueue;
            threadOptions.mFutureQueue = mFutureQueue;
            threadOptions.threadPoolToken = mThreadPoolToken;
            int maxStacksize = threadFactory.mMaxStackSize>0?threadFactory.mMaxStackSize:0;
            for (int i = 0; i < poolSize; i++)
            {
                if(maxStacksize!=0)
                    threadPool[i] = new CThread(new ParameterizedThreadStart(ThreadHandle), maxStacksize);
                else
                {
                    threadPool[i] = new CThread(new ParameterizedThreadStart(ThreadHandle));
                }
                if (threadFactory.ExcuteMethod != null) threadOptions.Runnable+=threadFactory.ExcuteMethod;
                threadPool[i].Name = identifiedName + "_" + i;
                threadPool[i].IsBackground = mThreadPoolInfo.isBackgound;
                //set thread mPriority 
                switch (mThreadPoolInfo.mPriority)
                {
                    case CThread.CThreadOption.Policy.LOWER:
                        threadPool[i].Priority = ThreadPriority.Lowest;
                        break;
                    case CThread.CThreadOption.Policy.NORMAL:

                        threadPool[i].Priority = ThreadPriority.Normal;
                        break;
                    case CThread.CThreadOption.Policy.MIDDLE_PRIORITY:

                        threadPool[i].Priority = ThreadPriority.AboveNormal;
                        break;
                    case CThread.CThreadOption.Policy.HIGHT_PRIORITY:
                        threadPool[i].Priority = ThreadPriority.Highest;
                        break;
                    default:
                        threadPool[i].Priority = ThreadPriority.Normal;
                        break;

                }
                threadPool[i].Start(threadOptions);
                activeThreads.Add(threadPool[i]);



            }
        }
        /// <summary>
        /// create a specific thread pool with requirements of thread name,pool size etc.
        /// </summary>
        /// <param name="name">thread pool identified name </param>
        /// <param name="size">pool size</param>
        /// <param name="doQueue">which execute work events by one thread of thread pool.</param>
        /// <param name="futureQueue">which public finished affair when a thread executed completely within thread pool.</param>
        /// <param name="factory">create a thread by <see cref="CThreadFactory">CThreadFactory</> thread pool which execute </param>
        /// <param name="threadPoolInfo">Need list of thread information(e.g. whether background thread or not,thread mPriority etc.) to establish thread pool.</param>
        public CThreadPool(string name, int size, CEventQueue doQueue, CEventQueue futureQueue,CThreadFactory factory, CThreadPoolInfo threadPoolInfo)
        {
            if(factory==null) throw new CException("factory variable do not null");
            poolSize = size;
            mDoQueue = doQueue;
            mFutureQueue = futureQueue;
            mThreadPoolInfo = threadPoolInfo;
            threadFactory = factory;
            mThreadPoolToken = new CThreadPoolInfo.ThreadPoolToken();
            identifiedName = name + "_" + GenerateUniqueNumber.New();
        }
        public void Start()
        {
            ActiveMain();
        }
        public void Cancel(bool cancel)
        {
            mThreadPoolToken.isCancel = cancel;
            DoCancel(true);
            cancel = true;

        }
        public bool isCancel()
        {
            return cancel;
        }
        public void DoCancel(bool cancel)
        {
            if (activeThreads.Count > 0)
            {
                for (int i = 0; i < activeThreads.Count; i++)
                {
                    //Monitor.Enter(_Lock);
                    if (activeThreads[i].IsAlive)
                    {
                        try
                        {
                            activeThreads[i].Abort();
                        }
                        catch (CException ex)
                        {

                            throw new CException("occured a error during stop thread[" + activeThreads[i] + "].Detail:"+ex.Message);
                        }
                        //activeThreads[]

                    }
                    UnityEngine.Debug.Log(activeThreads[i].Name + " is stopped.");
                    // Monitor.Exit(_Lock);
                }

            }
        }
        [Obsolete("Construction parameters in which already have this requirement")]
        public void Prepare(int id, CThreadPoolInfo threadInfos)
        {
            this.mThreadPoolInfo = threadInfos;
        }


        void Update()
        {

        }

        protected internal void ThreadHandle(Object Params)
        {
            var cTO = (CThread.CThreadOption)Params;
            WorkEventItem eventItem = null;
            while (!cTO.threadPoolToken.isCancel)
            {

                //get a work evnet from queue,in order to next step execute.
                // Interlocked.
                Interlocked.Increment(ref cTO.threadPoolToken.Counter);
                try
                {
                    if (cTO.mDoQueue != null && cTO.mDoQueue.Count() > 0)
                    {
                        UnityEngine.Debug.Log("Remaining execute queue:" + cTO.mDoQueue.Count());
                        UnityEngine.Debug.Log(cTO.mDoQueue.Count() + "The thread[id=" + Thread.CurrentThread.Name + "] is running at thread pool[" + Thread.CurrentThread.Name.Substring(0, Thread.CurrentThread.Name.IndexOf("_")) + "].");

                        eventItem = cTO.mDoQueue.Dequeue();
                        cTO.executingCount++;
                        //if(UnityEngine.Debug.isDebugBuild)
                        UnityEngine.Debug.Log(">>>>>>>>>>>>>>>>>>>>prepare to Execute :" + cTO.executingCount);
                    }
                }
                catch (CThreadException exception)
                {
                    eventItem.isError = true;
                    throw new CThreadException(exception.Message);
                }
                finally
                {
                    Interlocked.Decrement(ref cTO.threadPoolToken.Counter);

                }


                //thread to do work event
                lock (cTO._Lock)
                {


                    if (eventItem != null)
                    {
                        if (eventItem.isComplete != true)
                        {
                            eventItem.ExecuteBeforeCallback();
                            //case type
                            //execute custom method
                            if (cTO.Runnable != null)
                            {
                                cTO.Runnable(eventItem);
                            }
                            cTO.mFutureQueue.PushEvent(eventItem);
                            eventItem.ExecuteAfterCallback();

                            eventItem.RemoveAllCallback();
                            eventItem = null;
                        }
                    }
                }
            }
        }

        //----------------------Thread Pool Information--------------------------------------------------------------------------
        /// <summary>
        /// thread pool information
        /// </summary>
        public class CThreadPoolInfo
        {
            public bool isBackgound = false;
            public CThread.CThreadOption.Policy mPriority = CThread.CThreadOption.Policy.DEFAULT;
            public ManualResetEvent waitHandler = new ManualResetEvent(false);

            void RegistFinishQueue()
            {

            }

            public CThreadPoolInfo()
            {

            }
            public CThreadPoolInfo(CThread.CThreadOption.Policy priority, bool background)
            {
                mPriority=priority;
                isBackgound=background;
            }



            public class ThreadPoolToken
            {
                public bool isCancel = false;
                public bool isPause = false;
                public readonly object _Lock = new object();
                public int Counter = 0;

            }

        }



        /// <summary>
        /// 
        /// </summary>
        public class ThreadCompleteToken
        {

        }


        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public class ThreadPoolMediator : CEventQueue
        {
            public int groupSize;
            public void Initialize(int groupSize)
            {

            }


        }



        //-----------------------------CThread Factory---------------------------------------------------------------
        public class CThreadFactory
        {
            public Func<WorkEventItem,Object> ExcuteMethod;
       
           /// <summary>
           /// Carefully,if you don't understand thread stack size,please do not set a value,otherwise,will be affect performance on running-time.
           /// </summary>
            protected internal int mMaxStackSize=0;
            
           

            public CThreadFactory()
            {
                // TODO: Complete member initialization
            }
        }


        public class CPathFindingThread:CThread
        {
            public CPathFindingThread(ThreadStart start) : base(start)
            {
            }

            public CPathFindingThread(ThreadStart start, int maxStackSize) : base(start, maxStackSize)
            {
            }

            public CPathFindingThread(ParameterizedThreadStart start, int maxStackSize) : base(start, maxStackSize)
            {
            }

            public CPathFindingThread(ParameterizedThreadStart start) : base(start)
            {
            }
        }

        ///---------------------Thread-----------------------------------------------------------------------------
        /// <summary>
        /// just wrap system's thread
        /// </summary>
        public class CThread
        {

            private Thread thread;
            public string Name { get { return thread.Name; } set { thread.Name = value; } }
            public bool IsBackground { get { return thread.IsBackground; } set { thread.IsBackground = value; } }
            public ThreadPriority Priority { get { return thread.Priority; } set { thread.Priority = value; } }

            public bool IsAlive { get { return thread.IsAlive; } }
            /// <summary>
            /// Initializes a new instance of the <see cref="T:System.Threading.Thread"/> class.
            /// </summary>
            /// <param name="start">A <see cref="T:System.Threading.ThreadStart"/> delegate that represents the methods to be invoked when this thread begins executing. </param><exception cref="T:System.ArgumentNullException">The <paramref name="start"/> parameter is null. </exception>

            public CThread(ThreadStart start)
            {
                thread = new Thread(start);
            }
            /// <summary>
            /// Initializes a new instance of the <see cref="T:System.Threading.Thread"/> class, specifying the maximum stack size for the thread.
            /// </summary>
            /// <param name="start">A <see cref="T:System.Threading.ThreadStart"/> delegate that represents the methods to be invoked when this thread begins executing.</param><param name="maxStackSize">The maximum stack size, in bytes, to be used by the thread, or 0 to use the default maximum stack size specified in the header for the executable.Important   For partially trusted code, <paramref name="maxStackSize"/> is ignored if it is greater than the default stack size. No exception is thrown. </param><exception cref="T:System.ArgumentNullException"><paramref name="start"/> is null. </exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="maxStackSize"/> is less than zero.</exception>

            public CThread(ThreadStart start, int maxStackSize)
            {
                thread = new Thread(start, maxStackSize);
            }
            /// <summary>
            /// Initializes a new instance of the <see cref="T:System.Threading.Thread"/> class, specifying a delegate that allows an object to be passed to the thread when the thread is started and specifying the maximum stack size for the thread.
            /// </summary>
            /// <param name="start">A <see cref="T:System.Threading.ParameterizedThreadStart"/> delegate that represents the methods to be invoked when this thread begins executing.</param><param name="maxStackSize">The maximum stack size, in bytes, to be used by the thread, or 0 to use the default maximum stack size specified in the header for the executable.Important   For partially trusted code, <paramref name="maxStackSize"/> is ignored if it is greater than the default stack size. No exception is thrown.</param><exception cref="T:System.ArgumentNullException"><paramref name="start"/> is null. </exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="maxStackSize"/> is less than zero.</exception>

            public CThread(ParameterizedThreadStart start, int maxStackSize)
            {
                thread = new Thread(start, maxStackSize);
            }
            /// <summary>
            /// Initializes a new instance of the <see cref="T:System.Threading.Thread"/> class, specifying a delegate that allows an object to be passed to the thread when the thread is started.
            /// </summary>
            /// <param name="start">A <see cref="T:System.Threading.ParameterizedThreadStart"/> delegate that represents the methods to be invoked when this thread begins executing.</param><exception cref="T:System.ArgumentNullException"><paramref name="start"/> is null. </exception>

            public CThread(ParameterizedThreadStart start)
            {
                thread = new Thread(start);
            }
            /// <summary>
            /// Causes the operating system to change the state of the current instance to <see cref="F:System.Threading.ThreadState.Running"/>, and optionally supplies an object containing data to be used by the method the thread executes.
            /// </summary>
            /// <param name="parameter">An object that contains data to be used by the method the thread executes.</param><exception cref="T:System.Threading.ThreadStateException">The thread has already been started. </exception><exception cref="T:System.OutOfMemoryException">There is not enough memory available to start this thread. </exception><exception cref="T:System.InvalidOperationException">This thread was created using a <see cref="T:System.Threading.ThreadStart"/> delegate instead of a <see cref="T:System.Threading.ParameterizedThreadStart"/> delegate.</exception><filterpriority>1</filterpriority>

            public void Start(Object parameter)
            {
                thread.Start(parameter);
            }
            /// <summary>
            /// Causes the operating system to change the state of the current instance to <see cref="F:System.Threading.ThreadState.Running"/>.
            /// </summary>
            /// <exception cref="T:System.Threading.ThreadStateException">The thread has already been started. </exception><exception cref="T:System.OutOfMemoryException">There is not enough memory available to start this thread. </exception><filterpriority>1</filterpriority>

            public void Start()
            {
                thread.Start();
            }
            /// <summary>
            /// Raises a <see cref="T:System.Threading.ThreadAbortException"/> in the thread on which it is invoked, to begin the process of terminating the thread. Calling this method usually terminates the thread.
            /// </summary>
            /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception><exception cref="T:System.Threading.ThreadStateException">The thread that is being aborted is currently suspended.</exception><filterpriority>1</filterpriority><PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="ControlThread"/></PermissionSet>

            public void Abort()
            {
                thread.Abort();
            }
            /// <summary>
            /// Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping.
            /// </summary>
            /// <exception cref="T:System.Threading.ThreadStateException">The caller attempted to join a thread that is in the <see cref="F:System.Threading.ThreadState.Unstarted"/> state. </exception><exception cref="T:System.Threading.ThreadInterruptedException">The thread is interrupted while waiting. </exception><filterpriority>1</filterpriority>

            public void Join()
            {
                thread.Join();
            }




            public delegate Action Nothing(WorkEventItem eventItem);



            //---------------------Thread Options------------------------------------------
            /// <summary>
            /// create a thread necessarily with options
            /// </summary>
            public class CThreadOption
            {
                public string threadName;
                public CEventQueue mDoQueue;
                public CEventQueue mFutureQueue;
                /// <summary>
                /// how to deal this event which receive at a thread within thread pool.this function what thread token method(thread's main function) will invoke this method
                /// </summary>
                /// <param name="eventItem">receive a event</param>
                public Func<WorkEventItem, Object> Runnable;

                public ManualResetEvent waitHandler = new ManualResetEvent(false);
                public readonly object _Lock = new object();
                /// <summary>
                /// //the thread pool is able to control this thread,e.g. it could cancel after set   <see cref="CThreadPool.Cancle">CThreadPool.Cancle(bool cancel)</see> is true.
                /// </summary>
                public CThreadPoolInfo.ThreadPoolToken threadPoolToken;
                public long executingCount = 0L;

                /// <summary>
                /// thread mPriority
                /// </summary>
                public enum Policy
                {
                    NORMAL = 0,
                    DEFAULT = NORMAL,
                    LOWER = 1,
                    MIDDLE_PRIORITY = 2,
                    HIGHT_PRIORITY = 3
                }
            }

        }

    }


}